package com.syxbyi._2threadpool;

import java.io.*;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/*
copy线程和remove线程共用的“仓库”
 */
public class DirectoryTree {
    public DirectoryTreeNode root;
    public int treeSize;            // root不算入treeSize
    private ReentrantLock lock = new ReentrantLock();
    private Condition remover = lock.newCondition();

    public DirectoryTree(String rootPath) throws FileNotFoundException {
        root = new DirectoryTreeNode(null, new File(rootPath));
        root.copied = true;
        findNodes(root);
    }

    /**
     * 从文件树中拷贝一个文件到dest目录下
     * @param dest  目标目录
     * @return      是否还有未拷贝的文件，是返回true
     */
    public boolean copyFile(String dest) {
        BufferedInputStream bi = null;
        BufferedOutputStream bo = null;
        try {
            lock.lock();
            // 唤醒删除线程
            remover.signalAll();
            DirectoryTreeNode node = getNonFinishedNode(root);
            // node==null说明此时已经全部复制完成
            if (node == null)
                return false;
            String destPath = dest + getRelativePath(node);
            // 如果是文件夹，则创建
            if (node.file.isDirectory()) {
                File destFile = new File(destPath);
                destFile.mkdir();
            } else {
                bi = new BufferedInputStream(new FileInputStream(node.file));
                bo = new BufferedOutputStream(new FileOutputStream(destPath));
                byte[] buffer = new byte[1024];
                int len;
                while ((len = bi.read(buffer)) != -1) {
                    bo.write(buffer, 0, len);
                }
            }
            System.out.println("Copy   Thread " + Thread.currentThread().getId() + "\tcopy: " + node.file.getPath() + "\tto: " + destPath);
            node.copied = true;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        try {
            if (bi != null)
                bi.close();
            if (bo != null)
                bo.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return true;
    }

    public boolean removeFiles() {
        try {
            lock.lock();
            // 树上只剩root节点，删除完成
            if (treeSize == 0) {
                return false;
            }
            DirectoryTreeNode node = getLeafNode(root);
            // node==null说明拷贝线程没有完成，暂时没有节点可以删除
            if (node == null) {
                System.out.println("Remove Thread " + Thread.currentThread().getId() + "\twaiting");
                remover.await();
            } else {
                node.file.delete();
                System.out.println("Remove Thread " + Thread.currentThread().getId() + "\tremove: " + node.file.getPath());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return true;
    }

    /**
     * 拷贝进程用来查找第一个未被处理的文件（copied=false && lock=false），如果一个文件夹被标记为lock状态，则文件夹内部的文件都不能被访问
     * 传入参数作为父节点，首先判断该节点是否符合要求，否则遍历所有孩子节点，找出未加锁，且未处理完成的第一个孩子节点
     * @param node  正在遍历的树节点
     * @return      未处理的DirectoryTreeNode节点，null表示全部处理完毕（或者正在处理）
     */
    private DirectoryTreeNode getNonFinishedNode(DirectoryTreeNode node) {
        if (!node.copied)
            return node;
        if (node.children.size() == 0)
            return null;
        for (DirectoryTreeNode child: node.children) {
            DirectoryTreeNode nonFinished = getNonFinishedNode(child);
            if (nonFinished != null)
                return nonFinished;
        }
        return null;
    }

    /**
     * 查找第一个可以被删除的节点（叶节点，且拷贝完成），并从树上移除它
     * @param node  正在遍历的树节点
     * @return      可以被删除的节点，null说明没有可以删除的节点
     */
    private DirectoryTreeNode getLeafNode(DirectoryTreeNode node) {
        if (!node.copied)
            return null;
        // 查找所有孩子节点，如果没有孩子节点，返回自己
        if (node.children.size() == 0) {
            // 将node从树上删除
            node.parent.removeChild(node);
            return node;
        }
        // 如果有孩子节点，先删除孩子节点
        for (DirectoryTreeNode child: node.children) {
            DirectoryTreeNode leaf = getLeafNode(child);
            if (leaf != null)
                return leaf;
        }
        return null;
    }

    /**
     * 从给定根目录开始，查找目录下的所有文件，并创建对应的文件树
     * @param node  当前遍历节点
     * @throws FileNotFoundException    路径不存在或不是一个目录
     */
    private void findNodes(DirectoryTreeNode node) throws FileNotFoundException {
        File dir = node.file;
        if (!dir.exists()) {
            throw new FileNotFoundException("指定路径不存在");
        } else if (!dir.isDirectory()) {
            throw new FileNotFoundException("指定路径必须为目录");
        }
        File[] files = dir.listFiles();
        if (files.length == 0) return;
        for (File f: files) {
            DirectoryTreeNode child = new DirectoryTreeNode(node, f);
            node.addChild(child);
            treeSize++;
//            System.out.println("node: " + dir.getPath() + "\tchild: " + f.getPath());
            if (f.isDirectory()) {
                findNodes(child);
            }
        }
    }

    private String getRelativePath(DirectoryTreeNode node) {
//        System.out.println("root节点的绝对路径为：" + root.file.getAbsolutePath());
        int len = root.file.getAbsolutePath().length();
        //        System.out.println("node节点的相对路径为：" + path);
        return node.file.getAbsolutePath().substring(len);
    }
}
